<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>

    <link rel="stylesheet" type="text/css" 
      href="/assets/css/straybirds.css" media="screen" />
    <link rel="stylesheet" type="text/css" 
      href="/assets/css/pygments.css" media="screen" />

    <!-- MathJax Section Start -->

    <script type="text/javascript"
    src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
    </script>
    <script>
        MathJax.Hub.Config({
              tex2jax: {
              skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
              }
        });
        MathJax.Hub.Queue(function() {
            var all = MathJax.Hub.getAllJax(), i;
            for(i=0; i < all.length; i += 1) {
                all[i].SourceElement().parentNode.className += ' has-jax';
            }
        });
    </script>

    <!-- MathJax Section End -->

    <!-- Google Analytics Start-->
    <script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-48100787-1', 'minixalpha.github.io');
  ga('send', 'pageview');

</script>

    <!-- Google Analytics End -->

    <title>web.py 源代码分析之 web.test.application.testRedirect</title>
  </head>

  <body>
    <div class="container">
      <header>
        <div class="container">
          <h1>
              <a href="/" title="Home Page"> 潇湘夜雨 </a>
          <span class="github-src">
            <a href ="https://github.com/minixalpha/minixalpha.github.io"
               target="_blank"
               title="Fork me on GitHub">
              <img src="/assets/images/GitHub-Mark-Light-32px.png" alt="">
            </a>
          </span>
          </h1>
        </div>
      </header>

      <aside id="left-side">
        <h2> 分类 </h2>
  <ul class="category-list">
      
            
                <li style="background-color: #444">
                <a href="/categories/源代码阅读"> 源代码阅读 (20) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/设计模式"> 设计模式 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/科研"> 科研 (6) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/思想"> 思想 (2) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/技术"> 技术 (18) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/工具"> 工具 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/虚拟机"> 虚拟机 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/java"> java (11) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/c语言"> c语言 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/读书"> 读书 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/操作系统"> 操作系统 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/英语"> 英语 (10) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/计算机系统"> 计算机系统 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/生活"> 生活 (1) </a>
                </li>
            
      
  </ul>

      </aside>

      <aside id="right-side">
        <h2> 归档 </h2>
  <ul class="archive-list">
    
    
    
        
        
        
        
            
            <li>
                <a href="/2014/08">
                    2014-08 (1)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/07">
                    2014-07 (5)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/05">
                    2014-05 (12)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/04">
                    2014-04 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/03">
                    2014-03 (11)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/02">
                    2014-02 (6)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/01">
                    2014-01 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/11">
                    2013-11 (10)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/10">
                    2013-10 (3)
                </a>
            </li>

        
        
    
        
        
        
        
            
            <li>
                <a href="/2010/09">
                    2010-09 (1)
                </a>
            </li>

        
        
    
  </ul>

      </aside>

      <article>

<h1>分模块测试</h1>

<h2>application.py</h2>

<p>对 application.py 的测试，调用命令：</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">python test/application.py
</code></pre></div>
<h3>testRedirect</h3>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">testRedirect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">urls</span> <span class="o">=</span> <span class="p">(</span>
        <span class="s">&quot;/a&quot;</span><span class="p">,</span> <span class="s">&quot;redirect /hello/&quot;</span><span class="p">,</span>
        <span class="s">&quot;/b/(.*)&quot;</span><span class="p">,</span> <span class="s">r&quot;redirect /hello/\1&quot;</span><span class="p">,</span>
        <span class="s">&quot;/hello/(.*)&quot;</span><span class="p">,</span> <span class="s">&quot;hello&quot;</span>
    <span class="p">)</span>
    <span class="n">app</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">application</span><span class="p">(</span><span class="n">urls</span><span class="p">,</span> <span class="nb">locals</span><span class="p">())</span>
    <span class="k">class</span> <span class="nc">hello</span><span class="p">:</span>
        <span class="k">def</span> <span class="nf">GET</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span> 
            <span class="n">name</span> <span class="o">=</span> <span class="n">name</span> <span class="ow">or</span> <span class="s">&#39;world&#39;</span>
            <span class="k">return</span> <span class="s">&quot;hello &quot;</span> <span class="o">+</span> <span class="n">name</span>

    <span class="n">response</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">&#39;/a&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="s">&#39;301 Moved Permanently&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span>
        <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">&#39;Location&#39;</span><span class="p">],</span> 
        <span class="s">&#39;http://0.0.0.0:8080/hello/&#39;</span><span class="p">)</span>

    <span class="n">response</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">&#39;/a?x=2&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="s">&#39;301 Moved Permanently&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span>
        <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">&#39;Location&#39;</span><span class="p">],</span> 
        <span class="s">&#39;http://0.0.0.0:8080/hello/?x=2&#39;</span><span class="p">)</span>

    <span class="n">response</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">&#39;/b/foo?x=2&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="s">&#39;301 Moved Permanently&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span>
        <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">&#39;Location&#39;</span><span class="p">],</span> 
        <span class="s">&#39;http://0.0.0.0:8080/hello/foo?x=2&#39;</span><span class="p">)</span>
</code></pre></div>
<p>看到这段代码首先对 <code>urls</code> 挺好奇的，<code>urls</code> 一般是一个 <code>url</code> 对应
一个处理它的类，可是 <code>redirect /hello/</code> 是什么意思？所以，我们
有必要看一下 <code>web.application</code> 如何对 <code>urls</code> 进行处理。</p>

<p>我们还从这句开始：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="n">response</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">&#39;/a&#39;</span><span class="p">)</span>
</code></pre></div>
<p>看看 <code>urls</code> 是如何被处理的。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application.request</span>

<span class="k">def</span> <span class="nf">request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">localpart</span><span class="o">=</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
            <span class="n">host</span><span class="o">=</span><span class="s">&quot;0.0.0.0:8080&quot;</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">https</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>


    <span class="n">path</span><span class="p">,</span> <span class="n">maybe_query</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">splitquery</span><span class="p">(</span><span class="n">localpart</span><span class="p">)</span>
    <span class="n">query</span> <span class="o">=</span> <span class="n">maybe_query</span> <span class="ow">or</span> <span class="s">&quot;&quot;</span>

    <span class="o">...</span>

    <span class="n">env</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">HTTP_HOST</span><span class="o">=</span><span class="n">host</span><span class="p">,</span> <span class="n">REQUEST_METHOD</span><span class="o">=</span><span class="n">method</span><span class="p">,</span> <span class="n">PATH_INFO</span><span class="o">=</span><span class="n">path</span><span class="p">,</span> 
        <span class="n">QUERY_STRING</span><span class="o">=</span><span class="n">query</span><span class="p">,</span> <span class="n">HTTPS</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">https</span><span class="p">))</span>

    <span class="o">...</span>

    <span class="n">response</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">wsgifunc</span><span class="p">()(</span><span class="n">env</span><span class="p">,</span> <span class="n">start_response</span><span class="p">))</span>
</code></pre></div>
<p>可以看出，请求的 <code>url</code> 被分成两部分： <code>path</code> 和 <code>maybe_query</code>, 
然后传入 <code>env</code>中。</p>

<p><code>urllib</code> 是标准库的一部分，但是在文档中没有对 <code>splitquery</code>
有说明，这可能是一个非公开的API。通过</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">&gt;&gt;&gt; help(urllib.splitquery)
</code></pre></div>
<p>可以得到</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">splitquery(&#39;/path?query&#39;) --&gt; &#39;/path&#39;, &#39;query&#39;
</code></pre></div>
<p>看来这个调用是把 <code>url</code> 请求分成路径与请求两个部分，这也和
返回结果的赋值保持一致。</p>

<p>另外，最好不要使用这个函数，python 2.7 中提供了 <code>urlparse</code>
模块，可以完成同样功能（甚至更多），python 3 中这个模块
更改为 <code>urllib.parse</code>。</p>

<p>这里不再多说，只是明白这一句是要做什么就好。我们继续看数据
封装在 <code>env</code> 后发生的故事。</p>

<p>我们又来到这里：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python">    <span class="n">response</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">wsgifunc</span><span class="p">()(</span><span class="n">env</span><span class="p">,</span> <span class="n">start_response</span><span class="p">))</span>
</code></pre></div>
<p>最终对 <code>env</code> 的处理在 <code>wsgi</code> 函数中。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application.wsgifunc.wsgi</span>

        <span class="k">def</span> <span class="nf">wsgi</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">start_resp</span><span class="p">):</span>
            <span class="c"># clear threadlocal to avoid inteference of previous requests</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">_cleanup</span><span class="p">()</span>

            <span class="bp">self</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">env</span><span class="p">)</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="c"># allow uppercase methods only</span>
                <span class="k">if</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="o">!=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span><span class="p">:</span>
                    <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">nomethod</span><span class="p">()</span>

                <span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle_with_processors</span><span class="p">()</span>
                <span class="k">if</span> <span class="n">is_generator</span><span class="p">(</span><span class="n">result</span><span class="p">):</span>
                    <span class="n">result</span> <span class="o">=</span> <span class="n">peep</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">result</span><span class="p">]</span>
            <span class="k">except</span> <span class="n">web</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
                <span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">e</span><span class="o">.</span><span class="n">data</span><span class="p">]</span>


            <span class="n">result</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">safestr</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>

            <span class="n">status</span><span class="p">,</span> <span class="n">headers</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">headers</span>

            <span class="n">start_resp</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
</code></pre></div>
<p>同样，先把 <code>env</code> 载入 <code>web.ctx</code>， 然后我们通过 <code>print</code> 定位到
这一句改变了 <code>web.ctx.status</code> 的值。 </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">                result = self.handle_with_processors()
</code></pre></div>
<p>可见这里对 <code>url</code> 进行了分析。下面我们深入下去。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application.handle_with_processors</span>

<span class="k">def</span> <span class="nf">handle_with_processors</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">processors</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">processors</span><span class="p">:</span>
                <span class="n">p</span><span class="p">,</span> <span class="n">processors</span> <span class="o">=</span> <span class="n">processors</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">processors</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
                <span class="k">return</span> <span class="n">p</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">process</span><span class="p">(</span><span class="n">processors</span><span class="p">))</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span><span class="p">()</span>
        <span class="k">except</span> <span class="n">web</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">:</span>
            <span class="k">raise</span>
        <span class="k">except</span> <span class="p">(</span><span class="ne">KeyboardInterrupt</span><span class="p">,</span> <span class="ne">SystemExit</span><span class="p">):</span>
            <span class="k">raise</span>
        <span class="k">except</span><span class="p">:</span>
            <span class="k">print</span> <span class="o">&gt;&gt;</span> <span class="n">web</span><span class="o">.</span><span class="n">debug</span><span class="p">,</span> <span class="n">traceback</span><span class="o">.</span><span class="n">format_exc</span><span class="p">()</span>
            <span class="k">raise</span> <span class="bp">self</span><span class="o">.</span><span class="n">internalerror</span><span class="p">()</span>

    <span class="c"># processors must be applied in the resvere order. (??)</span>
    <span class="k">return</span> <span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">processors</span><span class="p">)</span>
</code></pre></div>
<p>这里 <code>processors</code> 为空，所以进入 <code>self.handle()</code></p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application.handle</span>

<span class="k">def</span> <span class="nf">handle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">fn</span><span class="p">,</span> <span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_match</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mapping</span><span class="p">,</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
    <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_delegate</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fvars</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
</code></pre></div>
<p>这个 <code>self.mapping</code> 是将我们的 <code>urls</code> 转化成两两一组后的列表。
先看看 <code>_match</code>函数。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application._match</span>

<span class="k">def</span> <span class="nf">_match</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mapping</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">pat</span><span class="p">,</span> <span class="n">what</span> <span class="ow">in</span> <span class="n">mapping</span><span class="p">:</span>
        <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">what</span><span class="p">,</span> <span class="n">application</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">value</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">pat</span><span class="p">):</span>
                <span class="n">f</span> <span class="o">=</span> <span class="k">lambda</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_delegate_sub_application</span><span class="p">(</span><span class="n">pat</span><span class="p">,</span> <span class="n">what</span><span class="p">)</span>
                <span class="k">return</span> <span class="n">f</span><span class="p">,</span> <span class="bp">None</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">continue</span>
        <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">what</span><span class="p">,</span> <span class="nb">basestring</span><span class="p">):</span>
            <span class="n">what</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">re_subm</span><span class="p">(</span><span class="s">&#39;^&#39;</span> <span class="o">+</span> <span class="n">pat</span> <span class="o">+</span> <span class="s">&#39;$&#39;</span><span class="p">,</span> <span class="n">what</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">re_compile</span><span class="p">(</span><span class="s">&#39;^&#39;</span> <span class="o">+</span> <span class="n">pat</span> <span class="o">+</span> <span class="s">&#39;$&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">result</span><span class="p">:</span> <span class="c"># it&#39;&#39;s a match</span>
            <span class="k">return</span> <span class="n">what</span><span class="p">,</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">result</span><span class="o">.</span><span class="n">groups</span><span class="p">()]</span>
    <span class="k">return</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span>
</code></pre></div>
<p>可以看出这是一个循环，根据当前的 <code>value</code> ，即 <code>web.ctx.path</code>
去查找 <code>urls</code> 中定义的对应项。what就是这个项。
先使用 <code>isinstance(what, application)</code> 看是不是使用了子程序。
再看看what是不是 <code>basestring</code> 的实例。直到运行至
result不为空，这说明找到了一个匹配。然后会返回匹配中
的所有分组。</p>

<p>当前的运行会选择第二个分支。
即：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application._match</span>

<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">what</span><span class="p">,</span> <span class="nb">basestring</span><span class="p">):</span>
    <span class="n">what</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">re_subm</span><span class="p">(</span><span class="s">&#39;^&#39;</span> <span class="o">+</span> <span class="n">pat</span> <span class="o">+</span> <span class="s">&#39;$&#39;</span><span class="p">,</span> <span class="n">what</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
</code></pre></div>
<p><code>utils.re_subm</code> 对路径中的正则表达式进行处理。<code>pat</code> 和 <code>what</code>
是 <code>urls</code> 中对应的项， <code>value</code> 是当前的请求路径。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># utils.re_subm</span>

<span class="k">def</span> <span class="nf">re_subm</span><span class="p">(</span><span class="n">pat</span><span class="p">,</span> <span class="n">repl</span><span class="p">,</span> <span class="n">string</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">    Like re.sub, but returns the replacement _and_ the match object.</span>

<span class="sd">        &gt;&gt;&gt; t, m = re_subm(&#39;g(oo+)fball&#39;, r&#39;f\\1lish&#39;, &#39;goooooofball&#39;)</span>
<span class="sd">        &gt;&gt;&gt; t</span>
<span class="sd">        &#39;foooooolish&#39;</span>
<span class="sd">        &gt;&gt;&gt; m.groups()</span>
<span class="sd">        (&#39;oooooo&#39;,)</span>
<span class="sd">    &quot;&quot;&quot;</span>
    <span class="n">compiled_pat</span> <span class="o">=</span> <span class="n">re_compile</span><span class="p">(</span><span class="n">pat</span><span class="p">)</span>
    <span class="n">proxy</span> <span class="o">=</span> <span class="n">_re_subm_proxy</span><span class="p">()</span>
    <span class="n">compiled_pat</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="n">proxy</span><span class="o">.</span><span class="n">__call__</span><span class="p">,</span> <span class="n">string</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">compiled_pat</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="n">repl</span><span class="p">,</span> <span class="n">string</span><span class="p">),</span> <span class="n">proxy</span><span class="o">.</span><span class="n">match</span>
</code></pre></div>
<p>这里 <code>re_compile(pat)</code> 的含义与 <code>re.complie(pat)</code> 类似，
返回一个<code>RegexObject</code> 对象，只不过加入了 <code>Cache</code> 机制，避免多次执行 <code>re.complie</code> 调用。</p>

<p>下面看这两行代码</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="n">proxy</span> <span class="o">=</span> <span class="n">_re_subm_proxy</span><span class="p">()</span>
<span class="n">compiled_pat</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="n">proxy</span><span class="o">.</span><span class="n">__call__</span><span class="p">,</span> <span class="n">string</span><span class="p">)</span>
</code></pre></div>
<p>其中 <code>_re_subm_proxy</code> 定义为：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">class</span> <span class="nc">_re_subm_proxy</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> 
        <span class="bp">self</span><span class="o">.</span><span class="n">match</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">match</span><span class="p">):</span> 
        <span class="bp">self</span><span class="o">.</span><span class="n">match</span> <span class="o">=</span> <span class="n">match</span>
        <span class="k">return</span> <span class="s">&#39;&#39;</span>
</code></pre></div>
<p><code>compiled_pat</code> 会与 <code>string</code> 一起生成一个 <code>Match</code> 对象，
这个对象会存储在一个 <code>_re_subm_proxy</code> 对象，即 <code>proxy</code>中
我们可以看到 <code>return</code> 中，<code>proxy</code> 最后会将其 <code>match</code> 返回。
我在想，为什么使用<code>search</code>直接生成一个 <code>Match</code>  对象然后返回呢？
查了一下，这似乎与 <code>代理模式</code> 相关。但具体为什么还不知道。又查了很久，似乎又与 <code>弱引用</code>, 和之前的 Cache 相关,
但是不确定。</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">问题：为什么要使用代理类
</code></pre></div>
<p>最后的 <code>return</code> 语句返回两个值，其中
<code>compiled_pat.sub (repl, string)</code> 是把 <code>string</code> 与 <code>pat</code>
中匹配的部分，用于替换 <code>repl</code> 中对应的组号。<code>proxy.match</code> 就是 <code>pat</code> 和 <code>string</code> 匹配得到的　<code>Mathc</code>对象。</p>

<p>关于 <code>Python 正则表达式</code> 可以参考这篇：
<a href="http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html">Python 正则表达式指南</a></p>

<p>到这里，我们可能就明白了一开始的时候的那段：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application._match</span>

<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">what</span><span class="p">,</span> <span class="nb">basestring</span><span class="p">):</span>
    <span class="n">what</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="n">utils</span><span class="o">.</span><span class="n">re_subm</span><span class="p">(</span><span class="s">&#39;^&#39;</span> <span class="o">+</span> <span class="n">pat</span> <span class="o">+</span> <span class="s">&#39;$&#39;</span><span class="p">,</span> <span class="n">what</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
</code></pre></div>
<p>的含义，它的意思是如何 <code>urls</code> 里有一对 <code>(url,class)</code>
其中 <code>url</code> 和　<code>class</code> 都是用正则表达式表示的，
这时候实际来了一个请求 <code>r_url</code>，它会与 <code>url</code>进行
匹配，根据这个匹配生成相应的 <code>r_class</code>。</p>

<p>看看刚才的示例就明白了：</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">&gt;&gt;&gt; t, m = re_subm(&#39;g(oo+)fball&#39;, r&#39;f\\1lish&#39;, &#39;goooooofball&#39;)
&gt;&gt;&gt; t
&#39;foooooolish&#39;
</code></pre></div>
<p>你可以把 <code>re_subm</code> 的三个参数依序当成 <code>url</code>, <code>class</code>，
以及 <code>r_url</code>，最后得到的 <code>t</code> 就是 <code>r_class</code>。</p>

<p>举个例子，假如有一系列页面，分别是 <code>req1</code>, <code>req2</code>, 
<code>req3</code>, ... , <code>reqN</code>, 需要处理。</p>

<p>那么就可以在 <code>urls</code> 里加入</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">(r&#39;req(\d+)&#39;, r&#39;proc\1&#39;)
</code></pre></div>
<p>这样，如果来了请求　<code>req2</code>，通过<code>re_subm</code>自然会解析成 <code>proc2</code>。</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">&gt;&gt;&gt; t, m = re_subm(r&#39;req(\d+)&#39;, r&#39;proc\1&#39;, &#39;req2&#39;)
&gt;&gt;&gt; t
&#39;proc2&#39;
&gt;&gt;&gt; m.group(0)
&#39;req2&#39;
&gt;&gt;&gt; m.group(1)
&#39;2&#39;
</code></pre></div>
<p>总之，</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">handle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">fn</span><span class="p">,</span> <span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_match</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mapping</span><span class="p">,</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
    <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_delegate</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fvars</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
</code></pre></div>
<p><code>self._match</code> 返回的是请求 <code>request(&#39;/a&#39;)</code> 与</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">python
        urls = (
            &quot;/a&quot;, &quot;redirect /hello/&quot;,
            &quot;/b/(.*)&quot;, r&quot;redirect /hello/\1&quot;,
            &quot;/hello/(.*)&quot;, &quot;hello&quot;
        )
</code></pre></div>
<p>进行匹配后的结果， <code>&#39;/a&#39;</code> 自然与 <code>urls[0]</code>
匹配，得到的 <code>fn</code> 是 <code>&quot;redirect /hello/&quot;</code>,
得到的 <code>args</code> 是 <code>[]</code>，因为<code>urls[0]</code>里面就没分组。</p>

<p>好了，现在进入 <code>self._delegate</code>，看看解析后的
<code>fn</code> 和　<code>args</code> 被如何处理。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">_delegate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">fvars</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">[]):</span>
    <span class="k">def</span> <span class="nf">handle_class</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
        <span class="n">meth</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span>
        <span class="k">if</span> <span class="n">meth</span> <span class="o">==</span> <span class="s">&#39;HEAD&#39;</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">meth</span><span class="p">):</span>
            <span class="n">meth</span> <span class="o">=</span> <span class="s">&#39;GET&#39;</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">meth</span><span class="p">):</span>
            <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">nomethod</span><span class="p">(</span><span class="n">cls</span><span class="p">)</span>
        <span class="n">tocall</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">cls</span><span class="p">(),</span> <span class="n">meth</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">tocall</span><span class="p">(</span>\<span class="o">*</span><span class="n">args</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">is_class</span><span class="p">(</span><span class="n">o</span><span class="p">):</span> <span class="k">return</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">ClassType</span><span class="p">,</span> <span class="nb">type</span><span class="p">))</span>

    <span class="k">if</span> <span class="n">f</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">notfound</span><span class="p">()</span>
    <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">application</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">f</span><span class="o">.</span><span class="n">handle_with_processors</span><span class="p">()</span>
    <span class="k">elif</span> <span class="n">is_class</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">handle_class</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
    <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="nb">basestring</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">f</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;redirect &#39;</span><span class="p">):</span>
            <span class="n">url</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39; &#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
            <span class="k">if</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&quot;GET&quot;</span><span class="p">:</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;QUERY_STRING&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">x</span><span class="p">:</span>
                    <span class="n">url</span> <span class="o">+=</span> <span class="s">&#39;?&#39;</span> <span class="o">+</span> <span class="n">x</span>
            <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
        <span class="k">elif</span> <span class="s">&#39;.&#39;</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">mod</span><span class="p">,</span> <span class="n">cls</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
            <span class="n">mod</span> <span class="o">=</span> <span class="nb">__import__</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span><span class="p">,</span> <span class="p">[</span><span class="s">&#39;&#39;</span><span class="p">])</span>
            <span class="n">cls</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span> <span class="n">cls</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">cls</span> <span class="o">=</span> <span class="n">fvars</span><span class="p">[</span><span class="n">f</span><span class="p">]</span>
        <span class="k">return</span> <span class="n">handle_class</span><span class="p">(</span><span class="n">cls</span><span class="p">)</span>
    <span class="k">elif</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">&#39;__call__&#39;</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">f</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">web</span><span class="o">.</span><span class="n">notfound</span><span class="p">()</span>
</code></pre></div>
<p>一眼就可以看出有一个 <code>elif</code> 分支对 <code>redirect</code> 进行了处理。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="nb">basestring</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">f</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;redirect &#39;</span><span class="p">):</span>
        <span class="n">url</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39; &#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&quot;GET&quot;</span><span class="p">:</span>
            <span class="n">x</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;QUERY_STRING&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">x</span><span class="p">:</span>
                <span class="n">url</span> <span class="o">+=</span> <span class="s">&#39;?&#39;</span> <span class="o">+</span> <span class="n">x</span>
        <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</code></pre></div>
<p>分析出 <code>redirect /hello/</code> 中的 <code>/hello/</code>，再看看
有没有查询字串，就是请求里有没有<code>?xx</code> 什么的。
得到最终的 <code>url</code> ，转给 <code>redirect</code> 函数处理。
直接找到 <code>webapi.py</code>，看到 <code>redirect</code> 的定义。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">class</span> <span class="nc">Redirect</span><span class="p">(</span><span class="n">HTTPError</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;A `301 Moved Permanently` redirect.&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="s">&#39;301 Moved Permanently&#39;</span><span class="p">,</span> <span class="n">absolute</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        Returns a `status` redirect to the new URL. </span>
<span class="sd">        `url` is joined with the base URL so that things like </span>
<span class="sd">        `redirect(&quot;about&quot;) will work properly.</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="n">newloc</span> <span class="o">=</span> <span class="n">urlparse</span><span class="o">.</span><span class="n">urljoin</span><span class="p">(</span><span class="n">ctx</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">url</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">newloc</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">absolute</span><span class="p">:</span>
                <span class="n">home</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">realhome</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">home</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">home</span>
            <span class="n">newloc</span> <span class="o">=</span> <span class="n">home</span> <span class="o">+</span> <span class="n">newloc</span>

        <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
            <span class="s">&#39;Content-Type&#39;</span><span class="p">:</span> <span class="s">&#39;text/html&#39;</span><span class="p">,</span>
            <span class="s">&#39;Location&#39;</span><span class="p">:</span> <span class="n">newloc</span>
        <span class="p">}</span>
        <span class="n">HTTPError</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">,</span> <span class="s">&quot;&quot;</span><span class="p">)</span>

<span class="n">redirect</span> <span class="o">=</span> <span class="n">Redirect</span>
</code></pre></div>
<p>这时候传过来的 <code>url</code> 是 <code>/hello/</code>。 注意 <code>status</code>
的默认值。
先使用 <code>urljoin</code> 得到一个绝对路径。官网上给出的
<code>urlparse.urljoin</code> 的例子是：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">urlparse</span> <span class="kn">import</span> <span class="n">urljoin</span>
<span class="o">&gt;&gt;&gt;</span> <span class="n">urljoin</span><span class="p">(</span><span class="s">&#39;http://www.cwi.nl/</span><span class="si">%7E</span><span class="s">guido/Python.html&#39;</span><span class="p">,</span> <span class="s">&#39;FAQ.html&#39;</span><span class="p">)</span>
<span class="s">&#39;http://www.cwi.nl/</span><span class="si">%7E</span><span class="s">guido/FAQ.html&#39;</span>
</code></pre></div>
<p>另外一个例子是针对第二个参数是绝对路径的情况的。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="o">&gt;&gt;&gt;</span> <span class="n">urljoin</span><span class="p">(</span><span class="s">&#39;http://www.cwi.nl/</span><span class="si">%7E</span><span class="s">guido/Python.html&#39;</span><span class="p">,</span>
<span class="o">...</span>         <span class="s">&#39;//www.python.org/</span><span class="si">%7E</span><span class="s">guido&#39;</span><span class="p">)</span>
<span class="s">&#39;http://www.python.org/</span><span class="si">%7E</span><span class="s">guido&#39;</span>
</code></pre></div>
<p>第二个例子的用法和我们当前的状况一致。所以得到的
<code>newloc</code> 还是 <code>/hello/</code>。</p>

<p>然后再查看 <code>ctx.home</code> 和 <code>ctx.realhome</code> 得到
新的　<code>newloc</code>。</p>

<p>在 <code>application.py</code> 中， <code>load</code> 函数定义了一些 <code>path</code>:</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="n">ctx</span><span class="o">.</span><span class="n">homedomain</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">protocol</span> <span class="o">+</span> <span class="s">&#39;://&#39;</span> <span class="o">+</span> <span class="n">env</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;HTTP_HOST&#39;</span><span class="p">,</span> <span class="s">&#39;[unknown]&#39;</span><span class="p">)</span>
<span class="n">ctx</span><span class="o">.</span><span class="n">homepath</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;REAL_SCRIPT_NAME&#39;</span><span class="p">,</span> <span class="n">env</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;SCRIPT_NAME&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">))</span>
<span class="n">ctx</span><span class="o">.</span><span class="n">home</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">homedomain</span> <span class="o">+</span> <span class="n">ctx</span><span class="o">.</span><span class="n">homepath</span>
<span class="c">#@@ home is changed when the request is handled to a sub-application.</span>
<span class="c">#@@ but the real home is required for doing absolute redirects.</span>
<span class="n">ctx</span><span class="o">.</span><span class="n">realhome</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">home</span>
</code></pre></div>
<p>从这可以看出，如果调用子程序　<code>ctx.home</code> 会改变，
但是　<code>ctx.realhome</code> 不会变，它用来在 redirect 时
生成绝对路径。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">if</span> <span class="n">newloc</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">absolute</span><span class="p">:</span>
        <span class="n">home</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">realhome</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">home</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">home</span>
    <span class="n">newloc</span> <span class="o">=</span> <span class="n">home</span> <span class="o">+</span> <span class="n">newloc</span>
</code></pre></div>
<p>现在可以理解了，如果 <code>absolute</code> 为真，那就用
<code>ctx.realhome</code> 和 <code>newloc</code> 组成新值，
如果不为真，直接用 <code>ctx.home</code>。这可能在使用子程序，
可以转向到以子程序为基础的<code>url</code> 中。</p>

<p>现在回到 <code>Redirect</code>。转到 <code>HTTPError</code>。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">class</span> <span class="nc">HTTPError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="p">{},</span> <span class="n">data</span><span class="o">=</span><span class="s">&quot;&quot;</span><span class="p">):</span>
        <span class="n">ctx</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">status</span>
        <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">headers</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
            <span class="n">header</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
        <span class="ne">Exception</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">status</span><span class="p">)</span>
</code></pre></div>
<p><code>ctx.status</code> 被设置，调用 <code>Exception</code>。</p>

<p>之后我们回到　<code>web.redirect(status)</code> 调用处。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">_delegate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">fvars</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">[]):</span>

    <span class="o">...</span>

    <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="nb">basestring</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">f</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;redirect &#39;</span><span class="p">):</span>
            <span class="n">url</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39; &#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
            <span class="k">if</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&quot;GET&quot;</span><span class="p">:</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;QUERY_STRING&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">x</span><span class="p">:</span>
                    <span class="n">url</span> <span class="o">+=</span> <span class="s">&#39;?&#39;</span> <span class="o">+</span> <span class="n">x</span>
            <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>


    <span class="o">...</span>
</code></pre></div>
<p>可以看出，这里把异常抛出。所以我们回到上一层，看看
相应的处理。</p>

<p>上一层：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">handle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">fn</span><span class="p">,</span> <span class="n">args</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_match</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">mapping</span><span class="p">,</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">path</span><span class="p">)</span>
    <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_delegate</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fvars</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
</code></pre></div>
<p>再上一层：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application.handle_with_processors</span>

<span class="k">def</span> <span class="nf">handle_with_processors</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">processors</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">processors</span><span class="p">:</span>
                <span class="n">p</span><span class="p">,</span> <span class="n">processors</span> <span class="o">=</span> <span class="n">processors</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">processors</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
                <span class="k">return</span> <span class="n">p</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">process</span><span class="p">(</span><span class="n">processors</span><span class="p">))</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle</span><span class="p">()</span>
        <span class="k">except</span> <span class="n">web</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">:</span>
            <span class="k">raise</span>
        <span class="k">except</span> <span class="p">(</span><span class="ne">KeyboardInterrupt</span><span class="p">,</span> <span class="ne">SystemExit</span><span class="p">):</span>
            <span class="k">raise</span>
        <span class="k">except</span><span class="p">:</span>
            <span class="k">print</span> <span class="o">&gt;&gt;</span> <span class="n">web</span><span class="o">.</span><span class="n">debug</span><span class="p">,</span> <span class="n">traceback</span><span class="o">.</span><span class="n">format_exc</span><span class="p">()</span>
            <span class="k">raise</span> <span class="bp">self</span><span class="o">.</span><span class="n">internalerror</span><span class="p">()</span>

    <span class="c"># processors must be applied in the resvere order. (??)</span>
    <span class="k">return</span> <span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">processors</span><span class="p">)</span>
</code></pre></div>
<p>再上一层：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c"># web.application.wsgifunc.wsgi</span>

        <span class="k">def</span> <span class="nf">wsgi</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">start_resp</span><span class="p">):</span>
            <span class="c"># clear threadlocal to avoid inteference of previous requests</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">_cleanup</span><span class="p">()</span>

            <span class="bp">self</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">env</span><span class="p">)</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="c"># allow uppercase methods only</span>
                <span class="k">if</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="o">!=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span><span class="p">:</span>
                    <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">nomethod</span><span class="p">()</span>

                <span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle_with_processors</span><span class="p">()</span>
                <span class="k">if</span> <span class="n">is_generator</span><span class="p">(</span><span class="n">result</span><span class="p">):</span>
                    <span class="n">result</span> <span class="o">=</span> <span class="n">peep</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">result</span><span class="p">]</span>
            <span class="k">except</span> <span class="n">web</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
                <span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">e</span><span class="o">.</span><span class="n">data</span><span class="p">]</span>


            <span class="n">result</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">safestr</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>

            <span class="n">status</span><span class="p">,</span> <span class="n">headers</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">headers</span>

            <span class="n">start_resp</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
</code></pre></div>
<p>这里终于看到了对异常的处理。这里 <code>e.data</code> 为空。我
们之前并没有设置这个值。</p>

<p>接着看后续处理, <code>safestr</code> 在 <code>utils.py</code> 中定义，
它负责把给定的对象转化成 <code>utf-8</code> 编码的字符串。
下一句设置 <code>status</code> 和 <code>headers</code> 的值。之前</p>

<p>我们已经看到 <code>web.ctx.status</code> 被设置了:</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">class</span> <span class="nc">HTTPError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="p">{},</span> <span class="n">data</span><span class="o">=</span><span class="s">&quot;&quot;</span><span class="p">):</span>
        <span class="n">ctx</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">status</span>
        <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">headers</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
            <span class="n">header</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
        <span class="ne">Exception</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">status</span><span class="p">)</span>
</code></pre></div>
<p><code>__init__</code> 中的 <code>status</code> 参数在上一层中设置。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">class</span> <span class="nc">Redirect</span><span class="p">(</span><span class="n">HTTPError</span><span class="p">):</span>
    <span class="sd">&quot;&quot;&quot;A `301 Moved Permanently` redirect.&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="s">&#39;301 Moved Permanently&#39;</span><span class="p">,</span> <span class="n">absolute</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
        <span class="sd">&quot;&quot;&quot;</span>
<span class="sd">        Returns a `status` redirect to the new URL. </span>
<span class="sd">        `url` is joined with the base URL so that things like </span>
<span class="sd">        `redirect(&quot;about&quot;) will work properly.</span>
<span class="sd">        &quot;&quot;&quot;</span>
        <span class="n">newloc</span> <span class="o">=</span> <span class="n">urlparse</span><span class="o">.</span><span class="n">urljoin</span><span class="p">(</span><span class="n">ctx</span><span class="o">.</span><span class="n">path</span><span class="p">,</span> <span class="n">url</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">newloc</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">absolute</span><span class="p">:</span>
                <span class="n">home</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">realhome</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">home</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">.</span><span class="n">home</span>
            <span class="n">newloc</span> <span class="o">=</span> <span class="n">home</span> <span class="o">+</span> <span class="n">newloc</span>

        <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
            <span class="s">&#39;Content-Type&#39;</span><span class="p">:</span> <span class="s">&#39;text/html&#39;</span><span class="p">,</span>
            <span class="s">&#39;Location&#39;</span><span class="p">:</span> <span class="n">newloc</span>
        <span class="p">}</span>
        <span class="n">HTTPError</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">,</span> <span class="s">&quot;&quot;</span><span class="p">)</span>

<span class="n">redirect</span> <span class="o">=</span> <span class="n">Redirect</span>
</code></pre></div>
<p>注意这里的　<code>status</code> 默认参数。</p>

<p>现在再回到 </p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">wsgi</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">start_resp</span><span class="p">):</span>
    <span class="c"># clear threadlocal to avoid inteference of previous requests</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">_cleanup</span><span class="p">()</span>

    <span class="bp">self</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">env</span><span class="p">)</span>

    <span class="k">try</span><span class="p">:</span>
        <span class="c"># allow uppercase methods only</span>
        <span class="k">if</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="o">!=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">method</span><span class="p">:</span>
            <span class="k">raise</span> <span class="n">web</span><span class="o">.</span><span class="n">nomethod</span><span class="p">()</span>


        <span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">handle_with_processors</span><span class="p">()</span>

        <span class="k">if</span> <span class="n">is_generator</span><span class="p">(</span><span class="n">result</span><span class="p">):</span>
            <span class="n">result</span> <span class="o">=</span> <span class="n">peep</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">result</span><span class="p">]</span>
    <span class="k">except</span> <span class="n">web</span><span class="o">.</span><span class="n">HTTPError</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
        <span class="n">result</span> <span class="o">=</span> <span class="p">[</span><span class="n">e</span><span class="o">.</span><span class="n">data</span><span class="p">]</span>


    <span class="n">result</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">safestr</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">result</span><span class="p">))</span>

    <span class="n">status</span><span class="p">,</span> <span class="n">headers</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">web</span><span class="o">.</span><span class="n">ctx</span><span class="o">.</span><span class="n">headers</span>

    <span class="n">start_resp</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">cleanup</span><span class="p">():</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">_cleanup</span><span class="p">()</span>
        <span class="k">yield</span> <span class="s">&#39;&#39;</span> <span class="c"># force this function to be a generator</span>

    <span class="k">return</span> <span class="n">itertools</span><span class="o">.</span><span class="n">chain</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">cleanup</span><span class="p">())</span>
</code></pre></div>
<p>随后调用　<code>start_resp</code> ，对 <code>status</code>, <code>headers</code>
进行处理。我们再回到上一层。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c">#web.application.request</span>

<span class="k">def</span> <span class="nf">request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">localpart</span><span class="o">=</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
                <span class="n">host</span><span class="o">=</span><span class="s">&quot;0.0.0.0:8080&quot;</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">https</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>

        <span class="o">...</span>

        <span class="n">response</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">storage</span><span class="p">()</span>
        <span class="k">def</span> <span class="nf">start_response</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">):</span>
            <span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">status</span>
            <span class="n">response</span><span class="o">.</span><span class="n">headers</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="n">headers</span><span class="p">)</span>
            <span class="n">response</span><span class="o">.</span><span class="n">header_items</span> <span class="o">=</span> <span class="n">headers</span>
        <span class="n">response</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="s">&quot;&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">wsgifunc</span><span class="p">()(</span><span class="n">env</span><span class="p">,</span> <span class="n">start_response</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">response</span>
</code></pre></div>
<p>这时候，<code>response</code> 中的 <code>stauts</code>　会设置好。
最后，返回最初的调用。</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="k">def</span> <span class="nf">testRedirect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="n">urls</span> <span class="o">=</span> <span class="p">(</span>
        <span class="s">&quot;/a&quot;</span><span class="p">,</span> <span class="s">&quot;redirect /hello/&quot;</span><span class="p">,</span>
        <span class="s">&quot;/b/(.*)&quot;</span><span class="p">,</span> <span class="s">r&quot;redirect /hello/\1&quot;</span><span class="p">,</span>
        <span class="s">&quot;/hello/(.*)&quot;</span><span class="p">,</span> <span class="s">&quot;hello&quot;</span>
    <span class="p">)</span>
    <span class="n">app</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">application</span><span class="p">(</span><span class="n">urls</span><span class="p">,</span> <span class="nb">locals</span><span class="p">())</span>
    <span class="k">class</span> <span class="nc">hello</span><span class="p">:</span>
        <span class="k">def</span> <span class="nf">GET</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span> 
            <span class="n">name</span> <span class="o">=</span> <span class="n">name</span> <span class="ow">or</span> <span class="s">&#39;world&#39;</span>
            <span class="k">return</span> <span class="s">&quot;hello &quot;</span> <span class="o">+</span> <span class="n">name</span>

    <span class="n">response</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s">&#39;/a&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="s">&#39;301 Moved Permanently&#39;</span><span class="p">)</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">assertEquals</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">&#39;Location&#39;</span><span class="p">],</span> <span class="s">&#39;http://0.0.0.0:8080/hello/&#39;</span><span class="p">)</span>
</code></pre></div>
<p><code>self.assertEquals</code> 会验证 <code>response.status</code> 的值。</p>

<p>所以，到这里，<code>testRedirect</code> 函数就分析结束了。</p>


      </article>

      <div class="comments">
        
          <div id="disqus_thread"></div>
 <script type="text/javascript">
     /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
     var disqus_shortname = 'minixalpha'; // required: replace example with your forum shortname

     /* * * DON'T EDIT BELOW THIS LINE * * */
     (function() {
         var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
    

        
      </div>


      <footer>
        Copyright (c) minixalpha 2014
      </footer>

    </div>
  </body>
</html>
